home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-12-18 | 49.3 KB | 1,719 lines | [TEXT/ALFA] |
- ## -*-Tcl-*-
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "bibtexMode.tcl"
- # created: 17/8/94 {9:12:06 am}
- # last update: 18/12/97 {5:33:11 pm}
- # Updated by: Vince Darley
- # E-mail: <darley@fas.harvard.edu>
- # mail: Division of Engineering and Applied Sciences, Harvard University
- # Oxford Street, Cambridge MA 02138, USA
- # www: <http://www.fas.harvard.edu/~darley/>
- #
- # Major rewrite of most of BibTeX mode. Original by Tom Pollard.
- # See the end of the BibTeX Help file for a history.
- #
- # ###################################################################
- ##
-
- alpha::mode Bib 1.0.1 bibtexMenu {*.bib *.inspec} { texMenu bibtexMenu } {
- addMenu bibtexMenu "•282"
- } uninstall {this-file} help {file "BibTeX Help"}
- # to make sure tex-mode is loaded
- texMenu
-
- newPref v bibAutoIndex 1 Bib "" [list "Never make index" \
- "Ask user when it is necessary" "Always remake when necessary"] index
-
- newPref v suffixString { \\\\} Bib
- newPref v prefixString {% } Bib
- newPref v fillColumn {65} Bib
- newPref f wordWrap {0} Bib
- newPref f autoMark {1} Bib
-
- ###########################################################################
- # Search patterns for entries and cite-keys
- #
- # set bibTopPat {^[ ]*@[a-zA-Z]+[\{\(]([-A-Za-z0-9_:/\.]+)}
- # match entry type
- set bibTopPat {^[ ]*@([a-zA-Z]+)[\{\(]}
- # match cite-key
- set bibTopPat1 {^[ ]*@[a-zA-Z]+[\{\(][ ]*([^=, ]+)}
- # match type and cite-key
- set bibTopPat2 {^[ ]*@([a-zA-Z]+)[\{\(][ ]*([^=, ]+)}
- # match first field (no cite-key)
- set bibTopPat3 {^[ ]*@([a-zA-Z]+)[\{\(]([ ]*[a-zA-Z]+[ ]*=[ ]*)}
-
- newPref v wordBreak {[a-zA-Z0-9]+} Bib
- newPref v wordBreakPreface {[^a-zA-Z0-9]} Bib
- newPref v funcExpr $bibTopPat Bib
-
- newPref f overwriteBuffer {1} Bib
- newPref f fieldBraces {1} Bib
- newPref f entryBraces {1} Bib
- newPref f segregateStrings {1} Bib
- newPref f markStrings {0} Bib
- newPref f alignEquals {0} Bib
- newPref f zapEmptyFields {0} Bib
- newPref f descendingYears {1} Bib
- newPref v indentString { } Bib
- newPref v stdAbbrevs {jan feb mar apr may jun jul aug sep oct nov dec} Bib
-
- # ◊◊◊◊ Option-click title bar ◊◊◊◊ #
- # use TeX routines for Bib mode
- proc Bib::OptionTitlebar {} {TeX::OptionTitlebar}
- proc Bib::OptionTitlebarSelect {item} {TeX::OptionTitlebarSelect $item}
-
- ###########################################################################
- # BibTeX Key Bindings.
- ###########################################################################
- # abbreviations: <o> = option, <z> = control, <s> = shift, <c> = command
- #
- bind 'b' <sz> selectEntry "Bib"
- bind 'n' <sz> nextEntry "Bib"
- bind 'p' <sz> prevEntry "Bib"
-
- bind 'f' <sz> searchFields "Bib"
- bind 'm' <sz> searchEntries "Bib"
- bind 'l' <sz> formatEntry "Bib"
-
- ###########################################################################
- # Data Definitions
- ###########################################################################
- ###########################################################################
- # Define the data arrays that contain the names of the required,
- # optional, and preferred fields for each entry type.
- #
- # The index names of the rqdFld() array _define_ the valid entry types
- # recognized by the program.
- #
- set rqdFld(article) {author title journal year}
- set optFld(article) {volume number pages month note}
- set myFld(article) {author title journal volume pages year note}
-
- set rqdFld(book) {author title publisher year}
- set optFld(book) {editor volume number series address edition month note}
-
- set rqdFld(booklet) {title}
- set optFld(booklet) {author howpublished address month year note}
-
- set rqdFld(conference) {author title booktitle year}
- set optFld(conference) {editor volume number series pages organization publisher address month note}
-
- set rqdFld(inBook) {author title chapter publisher year}
- set optFld(inBook) {editor pages volume number series address edition month type note}
-
- set rqdFld(inCollection) {author title booktitle publisher year}
- set optFld(inCollection) {editor volume number series type chapter pages address edition month note}
-
- set rqdFld(inProceedings) {author title booktitle year}
- set optFld(inProceedings) {editor volume number series pages organization publisher address month note}
-
- set rqdFld(manual) {title}
- set optFld(manual) {author organization address edition year month note}
-
- set rqdFld(mastersThesis) {author title school year}
- set optFld(mastersThesis) {address month note type}
-
- set rqdFld(misc) {}
- set optFld(misc) {author title howpublished year month note}
-
- set rqdFld(phdThesis) {author title school year}
- set optFld(phdThesis) {address month type note}
-
- set rqdFld(proceedings) {title year}
- set optFld(proceedings) {editor volume number series publisher organization address month note}
-
- set rqdFld(techReport) {author title institution year}
- set optFld(techReport) {type number address month note}
-
- set rqdFld(unpublished) {author title note}
- set optFld(unpublished) {year month}
-
- set entryNames [lsort [array names rqdFld]]
- set customEntries [lsort [array names myFld]]
-
- ###########################################################################
- # Define an array of flags indicating whether the data a given field
- # type should be quoted. The actual characters used to quote the field are
- # given by $bibOpenQuote and $bibCloseQuote, which are set by the routine
- # 'bibFieldDelims' according to the flag $fieldBraces.
- #
- # Note that the index names of the useBrace() array _define_ the valid
- # field types recognized by the program.
- #
- array set useBrace {
- address 1
- annote 1
- author 1
- booktitle 1
- chapter 0
- crossref 1
- edition 1
- editor 1
- howpublished 1
- institution 1
- journal 1
- key 1
- language 1
- month 1
- note 1
- number 0
- organization 1
- pages 0
- publisher 1
- school 1
- series 1
- title 1
- type 1
- volume 0
- year 0
- isbn 1
- customField 1
- city 1
- }
-
- set fieldNames [lsort [array names useBrace]]
- ###########################################################################
- # Default values for newly created fields
- #
- set defFldVal(language) "german"
-
- set fieldDefs [lsort [array names defFldVal]]
-
- ###########################################################################
- # BibTeX-mode mode definition
- ###########################################################################
-
- set bibtexKeyWords $fieldNames
- regModeKeywords -e {%} -m {@} -c red -k blue Bib $bibtexKeyWords
- unset bibtexKeyWords
-
- ###########################################################################
- # BibTeX Menu Definition.
- ###########################################################################
- proc bibtexMenu {} {}
-
- proc bibtex {} {
- global bibtexSig
- set name [app::launchAnyOfThese {BIBt Vbib} bibtexSig]
- switchTo [file tail $name]
- }
-
- menu -n $bibtexMenu {
- "bibtex"
- "(-)"
- {menu -n Entries -p makeEntry {}
- }
- {menu -n Fields -p makeField {}
- }
- "(-)"
- "selectEntry/B<U<B"
- "nextEntry/N<U<B"
- "prevEntry/P<U<B"
- "formatEntry/L<U<B"
- "copyCiteKey/C<U<B"
- "(-)"
- "searchEntries/M<U<B"
- "searchFields/F<U<B"
- {menu -n sortBy... -p bibSortProc {
- "citeKey"
- "firstAuthor,Year"
- "lastAuthor,Year"
- "year,FirstAuthor"
- "year,LastAuthor"
- }
- }
- {menu -n sortMarks... -p markSortProc {
- "alphabetically"
- "byPosition"
- }
- }
- "(-)"
- "countEntries"
- "formatAllEntries"
- "bibMakeIndex"
- "bibMakeDatabase"
- }
-
- menu -n Entries -p makeEntry [concat $entryNames {
- "(-)"
- "customEntry"
- } ]
-
- menu -n Fields -p makeField [concat $fieldNames {
- "(-)"
- "customField"
- "multipleFields"
- } ]
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bib_OpenFile" --
- #
- # Given a filename, and the directory of the base '.aux' file, try and
- # find the file. If we don't succeed, pass the request onto the TeX
- # code.
- # -------------------------------------------------------------------------
- ##
- proc bib_OpenFile {filename {dir ""}} {
- # look where base file was
- if {![catch {openFileQuietly "${dir}:${filename}"}]} {
- return
- }
- # look in bibtex inputs folder
- global bibtexSig
- if {![catch {openFileQuietly "[file dirname [nameFromAppl $bibtexSig]]:BibTeX inputs:${filename}"}]} {
- return
- }
- # look in all usual tex places
- openTeXFile "$filename"
- return
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bib_NoEntryExists" --
- #
- # No entry exists in the known .bib files. Either add an entry, possibly
- # in a new bibliography file, or add a .bib file to those currently
- # searched.
- # -------------------------------------------------------------------------
- ##
- proc bib_NoEntryExists {item {basefile ""}} {
- set basefile [bib_getBasefile $basefile]
- set choice [prompt \
- "No entry '$item' exists. What do you want to do?" \
- "New entry" "Choices" \
- "New entry" "New entry in new bibliography file" \
- "Add .bib file to \\bibliography\{…\}" \
- "Change original citation" \
- "Search all bibliographies" ]
- switch $choice {
- "New entry" {
- # need to pick a .bib file
- set bibfile [bibPickBibliography 1 \
- "Select a bibliography file to which to add an entry"]
- openTeXFile $bibfile
- global entryNames
- bibFormatSetup
- newEntry [listpick -p "Which type of entry?" $entryNames]
- insertText $item
- nextTabStop
- }
- "New entry in new bibliography file" {
- set bibfile [putfile "Save new bibliography as…" ".bib"]
- if {$bibfile == ""} {
- error "No bibliography file selected."
- } else {
- new -n $bibfile
- }
- global entryNames
- bibFormatSetup
- newEntry [listpick -p "Which type of entry?" $entryNames]
- insertText $item
- nextTabStop
- }
- "Add .bib file to \\bibliography\{…\}" {
- bib_insertNewBibliography $basefile
- }
- "Search all bibliographies" {
- alertnote "Not yet implemented"
- }
- "Change original citation" {
- bib_changeOriginalCitation $item $basefile
- }
- "Cancel" {
- # nothing
- }
- }
- }
-
- proc bib_changeOriginalCitation {citation {basefile ""}} {
- if {$basefile == ""} {set basefile [TeX_currentBaseFile]}
- # find .aux and open base .tex/.ltx
- if {[set proj [isWindowInFileset $basefile "tex"]] != ""} {
- set files [texListFilesInFileSet $proj]
- } else {
- set files $basefile
- }
- set got "[grep $citation $files]\r"
- if {[string first "; Line " $got] == [string last "; Line " $got]} {
- # just one match
- if ![regexp {∞([^\r\n]*)[\r\n]} $got dmy filename] {
- alertnote "I couldn't find the original. You probably have a\
- multi-part document which you haven't made into a TeX fileset.\
- Unless it's a fileset, I can't find the other files."
- return
- }
- openFileQuietly $filename
- eval select [searchInFile $filename $citation 1]
- message "This is the original citation. Change it, then re-run LaTeX and BibTeX."
- } else {
- grepsToWindow "* List of citations *" $got
- }
- }
-
- proc bib_getBasefile {{basefile ""}} {
- if {$basefile == ""} {return [TeX_currentBaseFile]}
- # find .aux and open base .tex/.ltx
- set base [file root $basefile]
- if [file exists ${base}.tex] {
- return ${base}.tex
- } elseif [file exists ${base}.ltx] {
- return ${base}.ltx
- } else {
- alertnote "Base file with name '${base}.tex/ltx' not found."
- error ""
- }
- }
-
- proc bib_insertNewBibliography {{basefile ""} {bibfile ""}} {
- set basefile [bib_getBasefile $basefile]
- openFileQuietly ${basefile}
-
- # find bibliography, position cursor and add
- pushPosition
- endOfBuffer
- if [catch {set pos [search -s -f 0 -r 0 -m 0 "\\bibliography\{" [getPos]]}] {
- # add the environment
- set pos [search -s -f 0 "\\end\{document\}" [getPos]]
- goto [lindex $pos 0]
- set preinsert "\\bibliography\{"
- set postinsert "\}\r\r"
- } else {
- set preinsert ""
- set postinsert ","
- goto [lindex $pos 1]
- }
- if {$bibfile == ""} {
- set bibfile [bibPickBibliography 0 \
- "Select a bibliography file to add"]
- }
- insertText "${preinsert}[lindex [split $bibfile "."] 0]${postinsert}"
- message "press <Ctrl .> to return to original cursor position"
- }
-
- # Used by bibPickBibliography to set a default in the listpick dialog
- # It's useful because you will often want to add a bunch of new items
- # in a row to the same bibliography.
- # NOTE: this is set by my code, not you.
- set Bib_defaultBib ""
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bibPickBibliography" --
- #
- # Put up a list-dialog so the user can select a bibliography file for
- # some action (taken by the caller). Can also create a new file if
- # desired.
- # -------------------------------------------------------------------------
- ##
- proc bibPickBibliography {{allowNew 1} {prompt "Pick a bibliography file"}} {
- set biblist [bibListAllBibliographies]
- if $allowNew {
- lappend biblist {New file…}
- }
- global Bib_defaultBib
- set bibfile [listpick -p $prompt -L $Bib_defaultBib $biblist]
- if {$bibfile == ""} {
- error "No bibliography file selected."
- } elseif {$bibfile == "New file…" } {
- set bibfile [putfile "Save new bibliography as…" ".bib"]
- if {$bibfile == ""} {
- error "No bibliography file selected."
- } else {
- set fout [open $bibfile w]
- close $fout
- }
- }
- return [file tail [set Bib_defaultBib $bibfile]]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bibListAllBibliographies" --
- #
- # Return all bibliographies on the search path. Optionally only return
- # those which are in a given .aux file.
- # -------------------------------------------------------------------------
- ##
- proc bibListAllBibliographies { {auxfile ""} } {
- TeXEnsureSearchPathSet
- global TeXSearchPath
- set biblist {}
- if {$auxfile == "" || [catch {set fid [open "$auxfile" r]}]} {
- foreach d $TeXSearchPath {
- eval lappend biblist [glob -nocomplain ${d}*.bib]
- }
- } else {
- set bibs {}
- # get list of bibs from .aux file
- set cid [scancontext create]
- scanmatch $cid {bibdata\{([^\}]*)\}} {
- eval lappend bibs [split $matchInfo(submatch0) ","]
- }
- scanfile $cid $fid
- close $fid
- scancontext delete $cid
- # find the full paths
- foreach b $bibs {
- foreach d $TeXSearchPath {
- if [file exists ${d}${b}.bib] {
- lappend biblist ${d}${b}.bib
- break
- }
- }
- }
- }
-
- return $biblist
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bibGotoEntry" --
- #
- # Look for a bib entry in the given list of files, or if that fails or
- # isn't given, look in all available bib files on the search path.
- # -------------------------------------------------------------------------
- ##
- proc bibGotoEntry {entry {biblist {}}} {
- if ![catch {bib_GotoEntryFromIndex $entry}] {
- return
- }
- if {[llength $biblist] && ![catch {bib_GotoEntry $entry $biblist 0}]} {
- return
- }
- if ![catch {bib_GotoEntry $entry [bibListAllBibliographies]}] {
- return
- }
- beep
- error "Can't find entry '$entry' in the .bib file(s)"
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bib_GotoEntryFromIndex" --
- #
- # Look in the bibIndex and find an entry very quickly.
- # -------------------------------------------------------------------------
- ##
- proc bib_GotoEntryFromIndex {entry} {
- set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
- global PREFS
- # if it fails, but we succeed later, we will have the opportunity
- # to rebuild the bibIndex
- if [file exists "${PREFS}:bibIndex"] {
- source "${PREFS}:bibIndex"
- global bibIndex
- foreach f [array names bibIndex] {
- if [regexp "\[ \r\n\]$entry\[ \r\n\]" "$bibIndex($f)"] {
- openFileQuietly $f
- set p [search -s -f 1 -r 1 $bibTopPat$entry 0]
- eval select $p
- refresh
- eval select $p
- unset bibIndex
- return
- }
- }
- unset bibIndex
- }
- error "Entry '$entry' not found in bibIndex"
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bib_FindAllEntries" --
- #
- # Find all entries with a given prefix, optionally attaching the titles
- # of the entries (this requires a bibDatabase file to be setup). Used
- # by TeX citation completions: \cite{Darley<cmd-Tab>
- # -------------------------------------------------------------------------
- ##
- proc bib_FindAllEntries {eprefix {withtitles 1}} {
- global PREFS
- set matches {}
- if $withtitles {
- if ![file exists "${PREFS}:bibDatabase"] {
- if {[askyesno "No bibDatabase exists, shall I make one?"]=="yes"} {
- bibMakeDatabase
- } else {
- error "No bib database exists"
- }
- }
- set cid [scancontext create]
- scanmatch $cid "^${eprefix}" {
- lappend matches $matchInfo(line)
- }
- set fid [open "${PREFS}:bibDatabase" r]
- scanfile $cid $fid
- close $fid
- scancontext delete $cid
- } else {
- if ![file exists "${PREFS}:bibIndex"] {
- if {[askyesno "No bibIndex exists, shall I make one?"]=="yes"} {
- bibMakeIndex
- } else {
- error "No bib index exists"
- }
- }
- global bibIndex
- source "${PREFS}:bibIndex"
- foreach f [array names bibIndex] {
- if { [set matched [univ::modeListCompletions $eprefix "bibIndex(${f})"]] != 0 } {
- eval lappend matches $matched
- }
- }
- unset bibIndex
- }
- return $matches
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bib_GotoEntry" --
- #
- # Find a bib entry in one of the given list of files, and signal an
- # error if the entry isn't found. I think this is the quickest way.
- # -------------------------------------------------------------------------
- ##
- proc bib_GotoEntry {entry biblist {rebuild 1}} {
- set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
- set cid [scancontext create]
- scanmatch $cid $bibTopPat$entry {
- set found "$matchInfo(offset)"
- }
- set found ""
- foreach f $biblist {
- message "Searching [file tail $f]…"
- if {![catch {set fid [open $f]}]} {
- scanfile $cid $fid
- close $fid
- if {$found != ""} {
- openFileQuietly $f
- goto $found
- refresh
- select $found [nextLineStart $found]
- scancontext delete $cid
- global BibmodeVars
- # make the index since it was obviously out of date
- if {$rebuild == 1 && ($BibmodeVars(bibAutoIndex) == 2 || [askyesno "The bibIndex seems to be out of date. Rebuild?"]=="yes")} {
- bibMakeIndex
- }
- return
- }
- }
- }
- scancontext delete $cid
- error "Entry '$entry' not found."
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bibMakeIndex" --
- #
- # Build the bibIndex file which allows for very fast lookup of bib
- # entries.
- # -------------------------------------------------------------------------
- ##
- proc bibMakeIndex {} {
- global PREFS
- set bibTopPat2 {^[ ]*@([a-zA-Z]+)[\{\(][ ]*([^=, ]+)}
- set cid [scancontext create]
- # this will actually mark strings as well
- scanmatch $cid $bibTopPat2 {
- if {[string tolower $matchInfo(submatch0)] != "string"} {
- lappend found $matchInfo(submatch1)
- }
- }
- set bout [open "${PREFS}:bibIndex" w]
- puts $bout "# Bibliography index file for quick reference lookup"
- puts $bout "# Created on [mtime [now]]"
- foreach f [bibListAllBibliographies] {
- set found {}
- puts $bout "set \"bibIndex($f)\" \{"
- message "Scanning [file tail $f]…"
- if {![catch {set fid [open $f]}]} {
- scanfile $cid $fid
- close $fid
- }
- # we sort so we can search it efficiently for all entries with
- # a given prefix.
- puts $bout " [lsort $found] "
- puts $bout "\}"
- }
- close $bout
- scancontext delete $cid
- message "bibIndex creation complete"
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bibMakeDatabase" --
- #
- # Build the bibDatabase which allows speedy completion of citations and
- # contains titles, so that you can pick the correct completion easily.
- # -------------------------------------------------------------------------
- ##
- proc bibMakeDatabase {} {
- set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
- global PREFS
- set bdatout [open "${PREFS}:bibDatabase" w]
- puts $bdatout "# Bibliography database file for quick reference lookup"
- puts $bdatout "# Created on [mtime [now]]"
- # if it fails, but we succeed later, we will have the opportunity
- # to rebuild the bibIndex
- foreach f [bibListAllBibliographies] {
- message "Scanning ${f}…"
- openFileQuietly $f
- set p 0
- while {![catch {search -s -f 1 -r 1 $bibTopPat $p} epos]} {
- set p [lindex $epos 1]
- set np [nextLineStart $p]
- set entry [string trim [getText $p $np] "\{\( \t\r,"]
- if ![catch {search -s -f 1 -r 1 {title[ \t]*=.*,[ \t]*\r} $np} epos] {
- set title [eval getText $epos]
- regsub -all "\[\r\t\]+" $title { } title
- set title [string range $title [string first "=" $title] end]
- set title [string trim $title " =\{\}\","]
- puts $bdatout "$entry \{$title\}"
- set p [lindex $epos 1]
- }
- }
- killWindow
- }
- close $bdatout
- }
-
-
- ###########################################################################
- # Menu command procs
- ###########################################################################
-
- proc makeField {menu item} {
- global fieldNames
- bibFormatSetup
-
- if {$item == "multipleFields"} {
- set flds [listpick -l -L {author year} -p "Pick desired fields:" $fieldNames]
- if {[llength flds]} {
- set lines {}
- foreach fld $flds {
- append lines [newField $fld]
- }
- } else {
- return
- }
- } else {
- set lines [newField $item]
- }
-
- set pos0 [nextLineStart [getPos]]
- goto $pos0
- elec::Insertion $lines
- }
-
- proc makeEntry {menu item} {
- bibFormatSetup
- newEntry $item
- }
-
- ###########################################################################
- # Return the bounds of the bibliographic entry surrounding the current
- # position.
- #
- proc getEntry {pos} {
-
- set pos1 [search -f 0 -r 1 -n -s {[ ]*@[a-zA-Z]*[\{\(]} $pos ]
- if {$pos1 == ""} {
- set begPos [nextLineStart $pos]
- set endPos $begPos
- } else {
- set begPos [lineStart [lindex $pos1 0]]
- set pos0 [lindex $pos1 1]
- set openBrace [getText [expr $pos0-1] $pos0 ]
- if {[catch {matchIt $openBrace $pos0} pos1]} {
- alertnote "There seems to be a badly delimited field in here. Are entry and field delimiters set correctly?"
- goto $begPos
- error "Can't find close brace"
- } else {
- set endPos [nextLineStart $pos1]
- }
- }
- return [list $begPos $endPos]
- }
-
- ###########################################################################
- # Advance to the next bibliographic entry.
- #
- proc nextEntry {} {
- global bibTopPat bibTopPat1 bibTopPat2
- # set topPat {[ ]*@([a-zA-Z]+)[\{\(]}
-
- set pos0 [lindex [getEntry [getPos]] 1]
- set nextPos [nextLineStart $pos0]
-
- while {![catch {search -f 1 -r 1 -s $bibTopPat $pos0} pos]} {
- regexp $bibTopPat [eval getText $pos] mtch type
- if {$type != "string"} {
- set nextPos [lindex $pos 0]
- break
- } else {
- set pos0 [nextLineStart [lindex $pos 1]]
- }
- }
- goto $nextPos
- }
-
- ###########################################################################
- # Go back to the previous bibliographic entry.
- #
- proc prevEntry {} {
- global bibTopPat bibTopPat1 bibTopPat2
- # set topPat {[ ]*@([a-zA-Z]+)[\{\(]}
-
- set pos0 [lindex [getEntry [getPos]] 0]
- if {$pos0 > 0} {
- set nextPos $pos0
- incr pos0 -1
- while {![catch {search -f 0 -r 1 -s $bibTopPat $pos0} pos]} {
- regexp $bibTopPat [eval getText $pos] mtch type
- if {$type != "string"} {
- set nextPos [lindex $pos 0]
- break
- } else {
- set pos0 [lineStart [lindex $pos 0]]
- if {$pos0 == 0} {break}
- incr pos0 -1
- }
- }
- goto $nextPos
- }
- }
-
- ###########################################################################
- # Select (highlight) the current bibliographic entry.
- #
- proc selectEntry {} {
- set pos [getEntry [getPos]]
- select [lindex $pos 0] [lindex $pos 1]
- }
-
- ###########################################################################
- # Put the cite-key of the current entry on the clipboard.
- #
- proc copyCiteKey {} {
- global bibTopPat2
- set limits [getEntry [getPos]]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
- if {[regexp -indices $bibTopPat2 [getText $top $bottom] allofit type citekey]} {
- select [expr $top+[lindex $citekey 0]] [expr $top+[lindex $citekey 1]+1]
- copy
- message "Copied \"[getSelect]\""
- }
- }
-
- ###########################################################################
- # Create a new bibliographic entry with its required fields.
- #
- proc newEntry {entryName} {
- global entryNames customEntries fieldNames rqdFld optFld myFld defFldVal
- global bibOpenEntry bibCloseEntry BibmodeVars
- goto [lindex [getEntry [getPos]] 1]
- if {$entryName == "customEntry"} {
- set lines "@••$bibOpenEntry••,\r"
- set theFields [listpick -l -L {author} -p "Pick desired fields:" $fieldNames]
- } else {
- set lines "@${entryName}$bibOpenEntry••,\r"
- if {[lsearch -exact $customEntries $entryName] >= 0 && [llength $myFld($entryName)]} {
- set theFields $myFld($entryName)
- } elseif {[lsearch -exact $entryNames $entryName] >= 0} {
- set theFields $rqdFld($entryName)
- } else {
- set theFields {}
- }
- }
- set nmlen 0
- foreach field $theFields {
- set len [string length $field]
- if {$len > $nmlen} {set nmlen $len}
- }
- set theTop [lineStart [getPos]]
- foreach field $theFields {
- catch {append lines [newField $field $nmlen]}
- }
- append lines "$bibCloseEntry\r"
- elec::Insertion $lines
- }
-
- ###########################################################################
- # Create a new field within the current bibliographic entry
- #
- proc newField {fieldName {nmlen 0}} {
- global fieldNames useBrace bibOpenQuote bibCloseQuote bibIndent
- global fieldDefs defFldVal
- set spc " "
- if {[lsearch -exact $fieldNames $fieldName] >= 0} {
- set needBraces $useBrace($fieldName)
- } else {
- set needBraces 1
- }
-
- if {[lsearch -exact $fieldDefs $fieldName] >= 0} {
- set val $defFldVal($fieldName)
- } else {
- set val "••"
- }
-
- if {$nmlen} {
- set pad [string range $spc 1 [expr $nmlen - [string length $fieldName]]]
- } else {
- set pad ""
- }
- if {$needBraces || $fieldName == "customField"} {
- set result "$bibIndent$fieldName =$pad ${bibOpenQuote}${val}${bibCloseQuote},\r"
- } else {
- set result "$bibIndent$fieldName =$pad $val,\r"
- }
- return $result
- }
-
- proc bibFormatSetup {} {
- global bibOpenQuote bibCloseQuote bibIndent BibmodeVars
- global bibOpenEntry bibCloseEntry bibAbbrevs
- bibFieldDelims
- bibEntryDelims
- set bibIndent $BibmodeVars(indentString)
- regsub {\\t} $bibIndent { } bibIndent
- set bibAbbrevs [listStrings]
- foreach abbrev $BibmodeVars(stdAbbrevs) {
- lappend bibAbbrevs [string tolower $abbrev]
- }
- }
-
- ###########################################################################
- # Find all entries that match a given regular expression and copy them to
- # a new buffer.
- #
- proc searchEntries {} {
- if [catch {prompt "Regular expression:" ""} reg] return
- if {![string length $reg]} return
- set reg ^.*$reg.*$
-
- set matches [findEntries $reg]
- if {[llength $matches] >0} {
- writeEntries $matches 0
- } else {
- message "No matching entries were found"
- }
- }
-
- ###########################################################################
- # Find all entries in which the indicated field matches a given regular
- # expression and copy them to a new buffer.
- #
- proc searchFields {} {
- global fieldNames
- if {[catch {eval prompt {{Field name:}} "author" {Fields} $fieldNames} fld]} return
- if {![string length $fld]} return
-
- if {[catch {prompt "Regular expression:" ""} reg]} return
- if {![string length $reg]} return
-
- set matches [findEntries $reg]
- if {[llength $matches] == 0} {
- return "No matching entries were found"
- }
-
- set vals {}
- foreach hit $matches {
- set pos [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- while {[set failure [expr {[getFldName $pos $top] != $fld}]] &&
- ![catch {search -f 1 -r 1 -i 1 -m 0 -l $bottom -s -- $reg $pos} mtch]} {
- set pos [lindex $mtch 1]
- }
- if {!$failure} { lappend vals [list $top $bottom] }
- }
-
- if {[llength $vals] >0} {
- writeEntries $vals 0
- } else {
- message "No matching entries were found"
- }
-
- }
-
- ###########################################################################
- # Sort all of the entries based on one of various criteria.
- #
- proc bibSortProc {menu item} {
- if {$item == "citeKey"} {
- sortByCiteKey
- } elseif {$item == "firstAuthor,Year"} {
- sortByAuthors 0 0
- } elseif {$item == "lastAuthor,Year"} {
- sortByAuthors 1 0
- } elseif {$item == "year,FirstAuthor"} {
- sortByAuthors 0 1
- } elseif {$item == "year,LastAuthor"} {
- sortByAuthors 1 1
- }
- }
-
- ###########################################################################
- # Sort the file marks. (These operations are also available under the
- # "Search:NamedMarks" menu)
- #
- proc markSortProc {menu item} {
- if {$item == "alphabetically"} {
- sortMarksFile
- } elseif {$item == "byPosition"} {
- orderMarks
- }
- }
-
- ###########################################################################
- # Sort all of the entries in the file alphabetically by author.
- #
- proc sortByAuthors {{lastAuthorFirst 0} {yearFirst 0}} {
- global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
- set bibSegStr $BibmodeVars(segregateStrings)
-
- set matches [findEntries $bibTopPat]
- set crossrefs [listCrossrefs]
- set strings [listStrings]
-
- set vals {}
- set others {}
- set refs {}
- set strs {}
-
- set beg [maxPos]
- set end 0
-
- foreach hit $matches {
- set pos [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
- if {[regexp $bibTopPat1 $entry allofit citeKey]} {
- set citeKey [string tolower $citeKey]
- set keyExists 1
- } else {
- set citekey ""
- set keyExists 0
- }
-
- if {$keyExists && [lsearch -exact $crossrefs $citeKey] >= 0} {
- lappend refs [list $pos $top $bottom]
- } elseif {$bibSegStr && $keyExists && [lsearch -exact $strings $citeKey] >= 0} {
- lappend strs [list $citeKey $top $bottom]
- } else {
- if {![catch {getFldValue $entry author} fldval]} {
- if {[catch {getFldValue $entry year} year]} { set year 9999 }
- lappend vals [list [authSortKey $fldval $lastAuthorFirst $year $yearFirst] $top $bottom]
- } else {
- lappend others [list $pos $top $bottom]
- }
- }
- if {$top < $beg} {set beg $top}
- if {$bottom > $end} {set end $bottom}
- }
-
- if {$bibSegStr} {
- set result [concat $strs $others [lsort $vals] $refs]
- } else {
- set result [concat $others [lsort $vals] $refs]
- }
-
- if {[llength $result] >0} {
- writeEntries $result 1 $beg $end
- } else {
- message "No results of author sort !!??"
- }
- }
-
- ###########################################################################
- # Return a list of the cite-keys of all cross-referenced entries.
- #
- proc listStrings {} {
- global bibTopPat bibTopPat1 bibTopPat2
- set matches [findEntries {^[ ]*@string *[\{\(]} 0]
-
- message "scanning for @strings…"
- foreach hit $matches {
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
- regexp $bibTopPat1 $entry allofit citekey
- set citekey [string tolower $citekey]
- if {[catch {incr strings($citekey)} num]} {
- set strings($citekey) 1
- }
- }
- if {[catch {lsort [array names strings]} res]} {
- set res {}
- }
- message ""
- return $res
- }
-
- ###########################################################################
- # Return a list of the cite-keys of all cross-referenced entries.
- #
- proc listCrossrefs {} {
- set matches [findEntries {crossref}]
- catch {unset crossrefs}
-
- message "scanning for crossrefs…"
- foreach hit $matches {
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
- if {![catch {getFldValue $entry crossref} fldval]} {
- set fldval [string tolower $fldval]
- if {[catch {incr crossref($fldval)} num]} {
- set crossrefs($fldval) 1
- }
- }
- }
- if {[catch {lsort [array names crossrefs]} res]} {
- set res {}
- }
- message ""
- return $res
- }
-
- ###########################################################################
- # Create a sort key from an author list. When sorting entries by author,
- # performing the sort using keys should be faster than reparsing the author
- # lists for every comparison (the old method :-( ).
- #
- proc authSortKey {authList lastAuthorFirst {year {}} {yearFirst 0}} {
- global BibmodeVars
- set pat1 {\\.\{([A-Za-z])\}}
- set pat2 {\{([^\{\}]+) ([^\{\}]+)\}}
-
- # Remove enclosing braces, quotes, or whitespace
- set auths %[string trim $authList {{}" }]&
- # Remove TeX codes for accented characters
- regsub -all $pat1 $auths {\1} auths
- # Concatenate strings enclosed in braces
- while {[regsub -all $pat2 $auths {{\1\2}} auths]} {}
- # Remove braces (curly and square)
- regsub -all {[][\{\}]} $auths {} auths
- # regsub -all {,} $auths { ,} auths
- # Replace 'and's with begin-name/end-name delimiters
- regsub -all {[ ]and[ ]} $auths { \&% } auths
- # Put last name first in name fields without commas
- regsub -all {%([^\&,]+) ([^\&, ]+) *\&} $auths {%\2,\1\&} auths
- # Remove begin-name delimiters
- regsub -all {%} $auths {} auths
- # Remove whitespace surrounding name separators
- regsub -all {[ ]*\&[ ]*} $auths {\&} auths
- # Replace whitespace separating words with shrieks
- regsub -all {[ ,]+} $auths {!} auths
- # If desired, move last author to head of sort key
- if {$lastAuthorFirst} {
- regsub {(.*)&([^&]+)&?$} $auths {\2\&\1} auths
- }
- # If provided, sort by year (descending order) as well
- regsub {^[^0-9]*([0-9]*).*$} $year {\1} year
- if {$year != {}} {
- if {$BibmodeVars(descendingYears)} { catch {set year [expr 9999-$year]} }
- if {$yearFirst} {
- set auths "$year&$auths"
- } else {
- regsub {^([^&]+)(&?)} $auths "\\1\\&${year}\\2" auths
- }
- }
-
- return $auths
- }
-
- ###########################################################################
- # Sort all of the entries in the file alphabetically by their cite-keys.
- #
- proc sortByCiteKey {} {
- global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
- set bibSegStr $BibmodeVars(segregateStrings)
-
- set matches [findEntries $bibTopPat]
- set crossrefs [listCrossrefs]
- set strings [listStrings]
-
- set begEntries [maxPos]
- set endEntries 0
-
- set strs {}
- set vals {}
- set refs {}
-
- foreach hit $matches {
- set beg [lindex $hit 0]
- set end [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- if {[regexp $bibTopPat1 [getText $top $bottom] allofit citekey]} {
- set citekey [string tolower $citekey]
- set keyExists 1
- } else {
- set citekey "000000$beg"
- set keyExists 0
- }
-
- if {$keyExists && [lsearch -exact $crossrefs $citekey] >= 0} {
- lappend refs [list $top $top $bottom]
- } elseif {$keyExists && $bibSegStr && [lsearch -exact $strings $citekey] >= 0} {
- lappend strs [list $citekey $top $bottom]
- } else {
- lappend vals [list $citekey $top $bottom]
- }
-
- if {$top < $begEntries} {set begEntries $top}
- if {$bottom > $endEntries} {set endEntries $bottom}
- }
-
- if {$bibSegStr} {
- set result [concat $strs [lsort $vals] $refs]
- } else {
- set result [concat [lsort $vals] $refs]
- }
-
- if {[llength $result] >0} {
- writeEntries $result 1 $begEntries $endEntries
- } else {
- message "No results of cite-key sort !!??"
- }
- }
-
- ###########################################################################
- # Search for all entries matching a given regular expression. The results
- # are returned in a list, each element of which is a list of four integers:
- # the beginning and end of the matching entry and the beginning and end of
- # the matching string. Adapted from "matchingLines" in "misc.tcl".
- #
- proc findEntries {reg {casesen 1}} {
- if {![string length $reg]} return
-
- set pos 0
- set result {}
- while {![catch {search -f 1 -r 1 -m 0 -i $casesen -s $reg $pos} mtch]} {
- set entry [getEntry [lindex $mtch 0]]
- lappend result [concat $mtch $entry]
- set pos [lindex $entry 1]
- }
- return $result
- }
-
- ###########################################################################
- # Return a list containing the data for the current entry, indexed by
- # the parameter names, e.g., "author", "year", etc. Index names for the
- # entry type and cite-key are "type" and "citekey".
- #
- proc getFields {pos} {
- global bibTopPat bibTopPat1 bibTopPat2 bibTopPat3
- set fldPat {[ ]*([a-zA-Z]+)[ ]*=[ ]*}
-
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- #
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
-
- if {[regexp -indices $bibTopPat2 $entry mtch theType theKey ]} {
- set key [string range $entry [lindex $theKey 0] [lindex $theKey 1]]
- set theRest [expr 1 + [lindex $mtch 1]]
- } elseif {[regexp -indices $bibTopPat3 $entry mtch theType aField]} {
- set key {}
- set theRest [lindex $aField 0]
- } else {
- error "Invalid entry"
- }
- lappend names type
- set type [string tolower [string range $entry [lindex $theType 0] [lindex $theType 1]]]
- lappend data [list $type]
-
- lappend names citekey
- lappend data $key
-
- set entry ",[string range $entry $theRest end]"
- set fldPat {,[ ]*([^ =,]+)[ ]*=[ ]*}
- set name {}
- while {[regexp -indices $fldPat $entry mtch sub1]} {
- set nextName [string range $entry [lindex $sub1 0] [lindex $sub1 1]]
- lappend names [string tolower $nextName]
- if {$name != ""} {
- set prevData [string range $entry 0 [expr [lindex $mtch 0]-1]]
- lappend data [breakIntoLines [bibFieldData $prevData]]
- }
- set name $nextName
- set entry [string range $entry [expr [lindex $mtch 1]+1] end]
- }
-
- lappend data [breakIntoLines [bibFieldData $entry]]
-
- return [list $names $data]
- }
-
- proc bibFieldData {text} {
- set text [string trim $text { ,#}]
- set text1 [string trim $text {\{\}\" }]
-
- if {[string match {*[\{\}\"]*} $text1]} {
- set words [parseWords $text]
- if {[llength $words]==1} {
- regsub {^[\{\"\']} $text {} text
- regsub {[\}\"\']$} $text {} text
- }
- } else {
- set text $text1
- }
- return $text
- }
-
-
- ###########################################################################
- # Extract the data from the indicated field of an entry, which is passed
- # as a single string. This version tries to be completely general,
- # allowing nested braces within data fields and ignoring escaped
- # delimiters. (derived from proc getField).
- #
- proc getFldValue {entry fldname} {
- set fldPat "\[ \]*${fldname}\[ \]*=\[ \]*"
- set fldPat2 {,[ ]*([^ =,]+)[ ]*=[ ]*}
- set slash "\\"
- set qslash "\\\\"
-
- set ok [regexp -indices -nocase $fldPat $entry mtch]
- if {$ok} {
- set pos [expr [lindex $mtch 1] + 1]
- set entry [string range $entry $pos end]
-
- if {[regexp -indices $fldPat2 $entry mtch sub1]} {
- set entry [string range $entry 0 [expr [lindex $mtch 0]-1]]
- }
- set fld [bibFieldData $entry]
-
- return $fld
-
- } else {
- error "field not found"
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it to the original
- # buffer in a canonical format
- #
- proc formatEntry {} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- set spc " "
-
- bibFormatSetup
-
- set pos [getPos]
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- if {![catch {bibFormatEntry $pos} result]} {
- if {$result != [getText $top $bottom]} {
- replaceText $top $bottom $result
- }
- goto $top
- nextEntry
- } else {
- message "Couldn't format this entry for some reason"
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it to the original
- # buffer in a canonical format
- #
- proc formatAllEntries {} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- set spc " "
-
- bibFormatSetup
-
- # This little dance handles the case that the first
- # entry starts on the first line
- #
- set hit [getEntry [getPos]]
- if {[lindex $hit 0] == [lindex $hit 1]} {
- nextEntry
- set hit [getEntry [getPos]]
- }
-
- while {[getPos] < [lindex $hit 1]} {
- set top [lindex $hit 0]
- set bottom [lindex $hit 1]
-
- if {![catch {bibFormatEntry $top} result]} {
- set oldEntry [getText $top $bottom]
- if {$result != $oldEntry} {
- deleteText $top $bottom
- insertText $result
- }
- }
- goto $top
- nextEntry
- set hit [getEntry [getPos]]
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it in a canonical format.
- # The formatted entry is returned.
- #
- proc bibFormatEntry {pos} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- global rqdFld optFld BibmodeVars bibAbbrevs
- set spc " "
- #
- # note: calling proc must call "bibFormatSetup" before calling "bibFormatEntry"
- #
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- if {[catch {getFields $pos} flds]} {
- error "bibFormatEntry: Getflds couldn't find any"
- }
-
- set names [lindex $flds 0]
- set vals [lindex $flds 1]
- set nfld [llength $names]
-
- set type [string tolower [lindex $vals 0]]
- set citekey [lindex $vals 1]
- # message "$citekey"
- # Don't process @string entries
- if {$type == "string"} {
- set lines [getText $top $bottom]
- return $lines
- }
- # Find length of longest field name
- set nmlen 0
- foreach nm $names {
- set len [string length $nm]
- if {$len > $nmlen} { set nmlen $len }
- if {![info exists useBrace($nm)]} { set useBrace($nm) 0 }
- }
-
- # Format first line
- set lines "@${type}${bibOpenEntry}${citekey},\r"
-
- # Format each field on a separate line
- for {set ifld 2} {$ifld < $nfld} {incr ifld} {
- set nm [lindex $names $ifld]
- set vl [lindex $vals $ifld]
- if {$vl != "" || ! $BibmodeVars(zapEmptyFields) ||
- [lsearch $rqdFld($type) $nm] >= 0} {
- set pad [expr $nmlen - [string length $nm]]
-
- if {$BibmodeVars(alignEquals)} {
- set pref "${bibIndent}$nm[string range $spc 1 $pad] ="
- } else {
- set pref "${bibIndent}$nm =[string range $spc 1 $pad]"
- }
- set ind [string range $spc 1 [string length $pref]]
-
- # Delimit field, if appropriate
- set noBrace [expr ($useBrace($nm) == 0 && [isNum $vl]) || [hasCat $vl]]
- if {$noBrace == 0 && [string first " " $vl] < 0} {
- set noBrace [expr [lsearch $bibAbbrevs [string tolower $vl]] >= 0]
- }
- if {$noBrace != 0} {
- set vl "$vl,"
- } else {
- set vl "${bibOpenQuote}${vl}${bibCloseQuote},"
- }
-
- set pieces [split $vl "\r"]
- append lines "$pref [lindex $pieces 0]\r"
- foreach piece [lrange $pieces 1 end] {
- append lines "$ind $piece\r"
- }
- }
- }
- append lines "$bibCloseEntry\r"
- return $lines
- }
-
- ###########################################################################
- # Get the name of the field that starts before the given position,
- # $pos. The positions $top and $bottom restrict the range of the
- # search for the beginning and end of the field; typically, $top and
- # $bottom will be the limits of a given entry.
- #
- proc getFldName {pos top} {
- set fldPat {[, ]+([^ =,\{\}\"\']+)[ ]*=[ ]*}
- if {![catch {search -f 0 -r 1 -m 0 -i 1 -s -limit $top "$fldPat" $pos} mtch]} {
- set theText [eval getText $mtch]
- regexp -nocase $fldPat $theText allofit fldnam
- return $fldnam
- } else {
- return {citekey}
- }
- }
-
- ###########################################################################
- # Set the quote characters for quoted fields based on the value of the
- # flag $bibUseBrace
- #
- proc bibFieldDelims {} {
- global BibmodeVars bibOpenQuote bibCloseQuote
- if {$BibmodeVars(fieldBraces)} {
- set bibOpenQuote "{"
- set bibCloseQuote "}"
- } else {
- set bibOpenQuote {"}
- set bibCloseQuote {"}
- }
- }
-
- proc bibEntryDelims {} {
- global BibmodeVars bibOpenEntry bibCloseEntry
- if {$BibmodeVars(entryBraces)} {
- set bibOpenEntry "{"
- set bibCloseEntry "}"
- } else {
- set bibOpenEntry "("
- set bibCloseEntry ")"
- }
- }
-
- proc isBibFile {} {
- set fileName [win::Current]
- set ext [file extension $fileName]
- return [string match ".bib" [string tolower $ext]]
- }
-
- proc hasNumVal {str} {
- expr ! [catch {expr $str}]
- }
- proc isNum {str} {
- regexp {^[ ]*[0-9]+[ ]*$} $str mtch
- }
- proc hasCat {str} {
- regexp {\#} $str mtch
- }
-
- ###########################################################################
- # Take a list of lists that point to selected entries and copy these into
- # a new window. The beginning and ending positions for each entry must
- # be the last two items in each sublist. The rest of the sublists are
- # ignored. It is assumed that each sublist has the same number of items.
- #
- proc writeEntries {entryPos nondestructive {beg {0}} {end {-1}}} {
- global BibmodeVars
- if {$end < 0} {set end [maxPos]}
- set llen [expr [llength [lindex $entryPos 0]] - 1]
- set llen1 [expr $llen-1]
- foreach entry $entryPos {
- set limits [lrange $entry $llen1 $llen]
- append lines [eval getText $limits]
- }
- set overwriteOK [expr $nondestructive || ! [isBibFile]]
- if {$BibmodeVars(overwriteBuffer) && $overwriteOK} {
- deleteText $beg $end
- insertText $lines
- goto $beg
- } else {
- set begLines [getText 0 [lineStart $beg]]
- set endLines [getText [nextLineStart $end] [maxPos]]
- new -n {*BibTeX Sort/Search*} -m Bib
- insertText $begLines
- insertText $lines
- insertText $endLines
- goto $beg
- setWinInfo dirty 0
- catch shrinkWindow
- }
- }
-
- ###########################################################################
- # Set a named mark for each entry, using the cite-key name
- #
- proc Bib::MarkFile {} {
- global BibmodeVars
- global bibTopPat bibTopPat1 bibTopPat2
- set pos 0
- while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat1 $pos} res]} {
- set start [lindex $res 0]
- set end [nextLineStart $start]
- set text [getText $start $end]
- set lab ""
- if {[regexp $bibTopPat2 $text mtch type citekey]} {
- if {[string tolower $type] != "string" || $BibmodeVars(markStrings)} {
- setNamedMark $citekey [lineStart [expr $start - 1]] $start $start
- }
- }
- set pos $end
- }
- }
-
- ###########################################################################
- # Report the number of entries of each type
- #
- proc countEntries {} {
- global entryNames
- global bibTopPat bibTopPat1 bibTopPat2
-
- set pos 0
- set count 0
- catch {unset type}
-
- while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat $pos} res]} {
- incr count
- set start [lindex $res 0]
- set end [nextLineStart $start]
- set text [getText $start $end]
- set lab ""
- if {[regexp $bibTopPat $text mtch entryType]} {
- set entryType [string tolower $entryType]
- if {[catch {incr type($entryType)} num]} {
- set type($entryType) 1
- }
- }
- set pos $end
- }
- new -n {*BibTeX Statistics*} -m Bib
- foreach name [lsort [array names type]] {
- if {$type($name) > 0} {
- append lines [format "%4.0d %s\n" $type($name) $name]
- }
- }
- append lines "---- -----------------\n"
- append lines [format "%4.0d %s\n" $count "Total entries"]
- insertText $lines
- goto 0
- setWinInfo dirty 0
- catch {shrinkWindow 1}
- }
- #--------------------------------------------------------------------------
- # command-double-clicking:
- #--------------------------------------------------------------------------
-
- ###########################################################################
- # In Bib mode, Cmd-double-clicks resolve abbrevs and cross-refs
- #
- proc Bib::DblClick {from to} {
- global bibTopPat bibTopPat1 bibTopPat2
-
- set limits [getEntry $from]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- # Extend selection to largest string that could be an entry reference
- set text [string trim [eval getText [BibExtendClick $from $to $top $bottom]]]
-
- # Get the citekey of current entry, so we can avoid jumping to it
- set citekey {}
- regexp $bibTopPat2 [getText $top $bottom] mtch type citekey ]
- set fldName [getFldName $from $top]
-
- if {[string length $text] == 0 || $text == $citekey || $fldName == $text ||
- ($fldName == "citekey" && [string tolower $type] != "string")} {
- message "Command-double-click on abbreviations and crossref arguments"
- return
- }
-
- # Jump to the mark for the specified citation, if a mark exists...
- # ...otherwise, do an ordinary search for the cite key
- pushPosition
- set searchPat "$bibTopPat\[ \]*[quote::Regfind $text]\[ ,\}\)\]"
- if {![catch {search -f 1 -r 1 -i 1 -m 0 $searchPat 0} mtch]} {
- goto [lindex $mtch 0]
- } else {
- popPosition
- select $from $to
- if {$fldName == "crossref"} {
- message "Cross-reference \"$text\" not found"
- } else {
- message "Command-double-click on abbreviations and crossref arguments"
- }
- return
- }
- message "Use Ctl-. to return to original position"
- return
- }
-
- # Extend the selection around the initial selection {$from,$to}
- # Extension is restricted to the range {$top,$bottom} (the current entry)
- proc BibExtendClick {from to top bottom} {
- if {$to == 0} { set to $from }
- set result [list $from $to]
- if {![catch {search -f 0 -r 1 -s -m 0 -l $top "\[,\{\]\"\'=" $from} mtch0]} {
- if {![catch {search -f 1 -r 1 -s -m 0 -l $bottom "\[,\}\]\"\'=" $to} mtch1]} {
- set from [lindex $mtch0 1]
- set to [lindex $mtch1 0]
- # Check for illegal chars embedded in the selection
- if {[regexp "\[\{\}\]=" [getText $from $to]] == 0} {
- set result [list $from $to]
- }
- }
- }
- return $result
- }
-
- #===============================================================================
- proc pcite {} {
- set words [getline "Citation keys" ""]
- if {![llength $words]} {error "No keys"}
-
- set pattern {@}
- foreach w $words {
- append pattern "(\[^@\]+$w)"
- }
-
- foreach entry [findEntries $pattern] {
- set res [getFields [car $entry]]
- set title [lindex [cadr $res] [lsearch [car $res] "title"]]
- set citekey [lindex [cadr $res] [lsearch [car $res] "citekey"]]
- set matches($title) $citekey
- set where($title) [car $entry]
- }
- if {![info exists matches]} {alertnote "No citations"; return}
- set title [listpick -p "Citation?" [lsort [array names matches]]]
- putScrap $matches($title)
- alertnote $matches($title)
- goto $where($title)
- }
-